/*
 * parser.c
 *
 *  Created on: Dec 14, 2010
 *      Author: cpy.prefers.you@gmail.com
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "clogo.h"
#include "symbol_table.h"
#include "buildin_commands.h"
#include "error_handling.h"
#include "symbol_table.h"
#include "lexer.h"
#include "parser.h"

static void CheckOperandType(Symbol_T *Operand, Symbol_T *Operator);
static void GetDeref(SymbolList_T **Token, Symbol_T *Symbol);

void GetSymbol(SymbolList_T **Token, Symbol_T *Symbol)
{
    CopySymbol(&Symbol, (*Token)->Symbol); /* for there MUST be allocated memory for Symbol */
    *Token = (*Token)->Next;
    return;
}

static void GetDeref(SymbolList_T **Token, Symbol_T *Symbol)
{
    SymbolList_T *TempSymbolList = NewSymbolList();
    SymbolList_T *TempSymbolListBackup = TempSymbolList;
    TempSymbolList->Symbol = NewSymbol();
    GetSymbol(Token, TempSymbolList->Symbol);
    TempSymbolList->Symbol->Name[0] = '"';
    TempSymbolList->Symbol->Type = TYPE_WORD;

    ((FunctionFP)_thing)(&TempSymbolList, Symbol);

    FreeSymbol(&TempSymbolListBackup->Symbol);
    free(TempSymbolListBackup);
    return;
}

static void ParseParenthesis(SymbolList_T **Token, Symbol_T *Item)
{
    Symbol_T *TempSymbol = NewSymbol();
    GetSymbol(Token, TempSymbol);

    switch ((*Token)->Symbol->Type)
    {
        case TYPE_PROCEDURE:
            GetSymbol(Token, Item);
            while ((*Token)->Symbol->Type != TYPE_R_PARENTHESIS)
                ((ProcedureFP)(Item->FunctionPointer))(Token);
            GetSymbol(Token, TempSymbol);
            break;
        case TYPE_NAME:
        case TYPE_FUNCTION:
        {
            GetSymbol(Token, TempSymbol);
            if (!TempSymbol->FunctionPointer)
                Error(ERROR_DONT_KNOW_HOW, TempSymbol->Name);

            SymbolList_T *TempSymbolList = NewSymbolList();
            SymbolList_T *TempSymbolListBackup = TempSymbolList;
            TempSymbolList->Symbol = NewSymbol();

            GetSymbol(Token, TempSymbolList->Symbol);

            while ((*Token)->Symbol->Type != TYPE_R_PARENTHESIS)
            {
                TempSymbolList->Next = (*Token);
                ((FunctionFP)(TempSymbol->FunctionPointer))(&TempSymbolList, TempSymbolListBackup->Symbol);
                (*Token) = TempSymbolList;
                TempSymbolList = TempSymbolListBackup;
            }

            CopySymbol(&Item, TempSymbolListBackup->Symbol);

            GetSymbol(Token, TempSymbol);
            TempSymbolList->Next = 0;
            FreeSymbolList(&TempSymbolList);
            break;
        }
        default:
            Item->Type = TYPE_EMPTY;
            strcpy(Item->Name, "[]");
            while ((*Token)->Symbol->Type != TYPE_R_PARENTHESIS)
                GetThing(Token, Item);
            GetThing(Token, TempSymbol);
            break;
    }
    FreeSymbol(&TempSymbol);
    return;
}

void GetItem(SymbolList_T **Token, Symbol_T *Item)
{
    if (!(*Token))
    {
        Item->Type = TYPE_EMPTY;
        strcpy(Item->Name, "[]");
        return;
    }
    switch ((*Token)->Symbol->Type)
    {
        case TYPE_ADDOP:
            if ((*Token)->Symbol->Name[0] == '+')
                CopySymbol(&Item, &(Symbol_T){ .Name = "\"1", .Type = TYPE_NUMBER });
            else if ((*Token)->Symbol->Name[0] == '-')
                CopySymbol(&Item, &(Symbol_T){ .Name = "\"-1", .Type = TYPE_NUMBER });
            (*Token)->Symbol->Precedence = PRECEDENCE_MULOP;
            (*Token)->Symbol->FunctionPointer = _mul;
            break;
        case TYPE_L_PARENTHESIS:
            ParseParenthesis(Token, Item);
            break;
        case TYPE_PROCEDURE:
            GetSymbol(Token, Item);
            if (!Item->FunctionPointer)
                Error(ERROR_DONT_KNOW_HOW, Item->Name);
            else
                ((ProcedureFP)(Item->FunctionPointer))(Token);
            break;
        case TYPE_NAME:
        case TYPE_FUNCTION:
            GetSymbol(Token, Item);
            if (!Item->FunctionPointer)
                Error(ERROR_DONT_KNOW_HOW, Item->Name);
            else
                ((FunctionFP)(Item->FunctionPointer))(Token, Item);
            break;
        case TYPE_WORD:
        case TYPE_NUMBER:
        case TYPE_BOOLEAN_TRUE:
        case TYPE_BOOLEAN_FALSE:
            GetSymbol(Token, Item);
            break;
        case TYPE_DEREF:
            GetDeref(Token, Item);
            break;
        default:
            GetSymbol(Token, Item);
            break;
    }
    return;
}

static void CheckOperandType(Symbol_T *Operand, Symbol_T *Operator)
{
    switch (Operator->Type)
    {
        case TYPE_ADDOP:
        case TYPE_MULOP:
            if (Operand->Type != TYPE_NUMBER)
                Error(ERROR_TYPE_MISMATCH, Operator->Name, Operand->Name);
            break;
        case TYPE_EQUAL_COMPOP:
        case TYPE_INEQUAL_COMPOP:
            if (!IS_DATUM(Operand))
                Error(ERROR_TYPE_MISMATCH, Operator->Name, Operand->Name);
            break;
        default:
            break;
    }
    return;
}

void GetExpression(SymbolList_T **Token, Symbol_T *Item)
{
    if (!*Token || !MatchType((*Token)->Symbol, 4, TYPE_ADDOP, TYPE_MULOP, TYPE_EQUAL_COMPOP, TYPE_INEQUAL_COMPOP))
        return;

    Symbol_T *Operator = NewSymbol();
    SymbolStack_T *OperatorStack = NewSymbolStack();
    SymbolStack_T *OperandStack = NewSymbolStack();
    Symbol_T *TempOperator = NewSymbol();
    Symbol_T *TempOperand1 = NewSymbol();
    Symbol_T *TempOperand2 = NewSymbol();

    PushStack(OperandStack, Item);

    while (*Token)
    {
        if (!MatchType((*Token)->Symbol, 4, TYPE_ADDOP, TYPE_MULOP, TYPE_EQUAL_COMPOP, TYPE_INEQUAL_COMPOP))
            break;
        GetSymbol(Token, Operator);
        if (!IsEmptyStack(OperatorStack))
        {
            PopStack(OperandStack, TempOperand2);
            while (!IsEmptyStack(OperatorStack))
            {
                PopStack(OperatorStack, TempOperator);
                if (Operator->Precedence < TempOperator->Precedence)
                {
                    PushStack(OperatorStack, TempOperator);
                    break;
                }
                PopStack(OperandStack, TempOperand1);
                CheckOperandType(TempOperand1, TempOperator);
                CheckOperandType(TempOperand2, TempOperator);
                ((OperationFP)(TempOperator->FunctionPointer))(TempOperand1, TempOperand2);
                CopySymbol(&TempOperand2, TempOperand1);
            }
            PushStack(OperandStack, TempOperand2);
        }
        GetItem(Token, TempOperand1);
        PushStack(OperandStack, TempOperand1);
        PushStack(OperatorStack, Operator);
    }

    PopStack(OperandStack, Item);
    while (!IsEmptyStack(OperatorStack))
    {
        PopStack(OperandStack, TempOperand1);
        PopStack(OperatorStack, TempOperator);
        CheckOperandType(TempOperand1, TempOperator);
        CheckOperandType(Item, TempOperator);
        ((OperationFP)(TempOperator->FunctionPointer))(TempOperand1, Item);
        CopySymbol(&Item, TempOperand1);
    }

    FreeSymbol(&Operator);
    FreeSymbolStack(&OperatorStack);
    FreeSymbolStack(&OperandStack);
    FreeSymbol(&TempOperator);
    FreeSymbol(&TempOperand1);
    FreeSymbol(&TempOperand2);
    return;
}

void GetThing(SymbolList_T **Token, Symbol_T *Thing)
{
    GetItem(Token, Thing);

    if (MatchType(Thing, 6, TYPE_WORD, TYPE_NUMBER, TYPE_BOOLEAN_TRUE, TYPE_BOOLEAN_FALSE, TYPE_ARRAY, TYPE_LIST))
        GetExpression(Token, Thing);

    return;
}
